package com.bdf.congcache.xml;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.*;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;


public class XPathParser
{
	//Document 接口表示整个 HTML 或 XML 文档。从概念上讲，它是文档树的根，并提供对文档数据的基本访问。
	private final Document document;

	private boolean validation;
	//文档声明解析器 通过DTD对文档进行解析
	private EntityResolver entityResolver;
	// 配置文件
	private Properties variables;

	//XPath 确定xml文档中的内容
	private XPath xpath;

	public XPathParser(String xml)
	{
		commonConstructor(false, null, null);
		this.document = createDocument(new InputSource(new StringReader(xml)));
	}

	public XPathParser(Reader reader)
	{
		commonConstructor(false, null, null);
		this.document = createDocument(new InputSource(reader));
	}

	/**
	 * @description: 将xml转换成输入流， 创建构造对象 document
	 * @author: 田培融
	 * @date: 2019/7/20 19:10
	  * @param inputStream 获取xml文件流
	 */
	public XPathParser(InputStream inputStream)
	{
        // 初始化成员对象
		commonConstructor(false, null, null);
		this.document = createDocument(new InputSource(inputStream));
	}

	/**
	 * @description:
	 * @author: 田培融
	 * @date: 2019/7/20 19:20
	  * @param expression
	 * @return:
	 */
	public XNode evalNode(String expression)
	{
		return evalNode(document, expression);
	}

	public XNode evalNode(Object root, String expression)
	{
		Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
		if (node == null)
		{
			return null;
		}
		return new XNode(this, node, variables);
	}

	private Object evaluate(String expression, Object root, QName returnType)
	{
		try
		{
			return xpath.evaluate(expression, root, returnType);
		}
		catch (Exception e)
		{
			throw new XmlParserException("Error evaluating XPath.  Cause: " + e, e);
		}
	}

    /**
     * 输入流资源转成Document文档
     * @param inputSource 输入流转换成输入流资源
     * @return Document文档， xml文件对象
     */
	private Document createDocument(InputSource inputSource)
	{

		try
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setValidating(validation);

			factory.setNamespaceAware(false);
			factory.setIgnoringComments(true);
			factory.setIgnoringElementContentWhitespace(false);
			factory.setCoalescing(false);
			factory.setExpandEntityReferences(true);

			DocumentBuilder builder = factory.newDocumentBuilder();
			builder.setEntityResolver(entityResolver);
			builder.setErrorHandler(new ErrorHandler() {
				@Override
				public void error(SAXParseException exception) throws SAXException
				{
					throw exception;
				}

				@Override
				public void fatalError(SAXParseException exception) throws SAXException
				{
					throw exception;
				}

				@Override
				public void warning(SAXParseException exception) throws SAXException
				{
				}
			});
			return builder.parse(inputSource);
		}
		catch (Exception e)
		{
			throw new XmlParserException("Error creating document instance.  Cause: " + e, e);
		}
	}


	/**
	 * @description:  xpath构造器
	 * @author: 田培融
	 * @date: 2019/7/20 19:00
	  * @param validation  确认 批准
	 * @param variables  配置文件
	 * @param entityResolver 文件分析器
	 * @return: void
	 */
	private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver)
	{
		this.validation = validation;
		this.entityResolver = entityResolver;
		this.variables = variables;
		XPathFactory factory = XPathFactory.newInstance();
		this.xpath = factory.newXPath();
	}

}
